﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.ServiceModel;
using System.Text;
using System.Threading;
using Universe.IoC;

namespace Universe.WCF.Behaviors.WcfStreaming
{
    public class BinaryWriterAdapter
    {
        private Guid _token;
        private ConfigurationContainer _config;
        private Stream _wcfResponse;

        public Guid Token
        {
            get { return _token; }
        }

        public Stream WcfResponseStream
        {
            get { return _wcfResponse; }
        }

        public BinaryWriterAdapter(Guid token, ConfigurationContainer config)
        {
            _token = token;
            _config = config;
        }

        public BinaryWriterAdapter(Guid token) : this(token, AdapterEnvironment.Default)
        {
        }

        public void Run(Action<BinaryWriter> dataWriter)
        {
            string bufferPath = _config.ResolveRequired<DirectoryInfo>().FullName;
            string dataName = Path.Combine(bufferPath, _token.ToString("N") + ".buffer");

            FileStream fileStream = new FileStream(dataName, FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
            LengthReference lenRef = new LengthReference();
            var statusStorage = _config.ResolveRequired<IResponseStatusStorage>();
            HashWriterStream writer = new HashWriterStream(
                fileStream, 
                lenRef, 
                _token, 
                statusStorage
                );

            // Starting Progress
            {
                statusStorage.StoreResponseStatus(_token, new ResponseStatus
                {
                    Exception = null,
                    Hash = null,
                    Length = 0,
                    State = ResponseState.InProgress,
                    Token = _token
                });
            }

            var hashAlg = _config.ResolveRequired<HashAlgorithm>();
            writer.RequireHash(hashAlg);
            int hashBufferSize = _config.ResolveRequired<int>();
            BufferedStream bwriter = new BufferedStream(writer, hashBufferSize);

            FileStream reader = new FileStream(dataName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
            ManualResetEvent isDone = new ManualResetEvent(false);
            WcfResponseStream responseStream = new WcfResponseStream(reader, isDone, dataName);
            responseStream.LenRef = lenRef;
            BinaryWriter binaryWriter = new BinaryWriter(bwriter, Encoding.UTF8);

            ThreadPool.QueueUserWorkItem(state =>
            {
                bool hasException = false;
                try
                {
                    /// GetMyStream_Impl(binaryWriter, size);
                    dataWriter(binaryWriter);
                    binaryWriter.Flush();
                    bwriter.Close();
                    string hash = string.Join("", writer.Hash.Select(x => x.ToString("X2")).ToArray());
                    statusStorage.StoreResponseStatus(_token, new ResponseStatus()
                    {
                        State = ResponseState.Success,
                        Hash = hash,
                        Exception = null,
                        Length = lenRef.Length,
                        Token = Token
                    });
                        

                    // Console.WriteLine("SERVER HASH: " + hash);
                }
                catch (Exception ex)
                {
#if DEBUG
                    Console.WriteLine("FAIL: " + ex);
#endif
                    statusStorage.StoreResponseStatus(_token, new ResponseStatus()
                    {
                        State = ResponseState.Failed,
                        Hash = null,
                        Exception = new ExceptionDetail(ex),
                        Length = lenRef.Length,
                        Token = Token
                    });

                    hasException = true;
                    bwriter.Close();
                    responseStream.Close();
                }
                finally
                {
                    // Если нет Эксепшн, то responseStream закрывает WCF
                    isDone.Set();
                    if (!hasException)
                        bwriter.Close();
                }

            });

            _wcfResponse = responseStream;
        }
    }
}
